package com.wlz.basic.memorySpace;


/**
 *  在虚拟机接收到指令，首先会从classPath 中加载这个MyTest.class ,读取这个文件的二进制，然后把Appmain类的类信息存放到运行时数据区的方法区中。
 *  这一过程称为MyTest类的加载过程。
 *      在解释上面的以前我们来回来一个经常的面试题
 *
 *          String a = "str"  和  String b = new String（“str”）  ,   a == b ?
 *
 *      上面的答案是   flase ,为什么呢？
 *
 *      == 比较的是栈中的引用地址
 *
 *      1.String a = "str" ------------   首先虚拟机会从方法区中去寻找是否有，有的话就直接把地址赋值给a ,没有的话，
 *      就在方法区中开辟一个内存在存储.(方法区的回收不会那么频繁，只有当内存不够的时候才会销毁不用的常量，非静态的)
 *
 *      2.String b = new String("str)  他是直接在堆内存中，直接开辟一个内存来存储的，把地址给 b
 *
 *      所以  a == b = false
 *
 *      看过上面的现在讲解下
 *
 *      s1 , s2 因为是在加载的时候，内容都一样所以肯定相等
 *
 */
public class MemorySpaceTest {

    public static void main(String[] args) {

        String s1 = "Hello";
        String s2 = "Hello";
        String s3 = "Hel" + "lo";
        String s4 = "Hel" + new String("lo");
        String s5 = new String("Hello");
        String s6 = s5.intern();
        String s7 = "H";
        String s8 = "ello";
        String s9 = s7 + s8;

        System.out.println(s1 == s2);  // true
        // s1 == s3这个地方有个坑，s3虽然是动态拼接出来的字符串，但是所有参与拼接的部分都是已知的字面量，在编译期间，这种拼接会被优化，编译器直接帮你拼好，
        // 因此String s3 = "Hel" + "lo";在class文件中被优化成String s3 = "Hello"，所以s1 == s3成立。
        // 只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中
        System.out.println(s1 == s3);  // true

        // s1 == s4当然不相等，s4虽然也是拼接出来的，但new String("lo")这部分不是已知字面量，是一个不可预料的部分，编译器不会优化，必须等到运行时才可以确定结果，
        // 结合字符串不变定理，鬼知道s4被分配到哪去了，所以地址肯定不同。对于所有包含new方式新建对象（包括null）的“+”连接表达式，它所产生的新对象都不会被加入字符串池中。
        System.out.println(s1 == s4);  // false

        // s1 == s9也不相等，道理差不多，虽然s7、s8在赋值的时候使用的字符串字面量，但是拼接成s9的时候，s7、s8作为两个变量，都是不可预料的，编译器毕竟是编译器，
        // 不可能当解释器用，不能在编译期被确定，所以不做优化，只能等到运行时，在堆中创建s7、s8拼接成的新字符串，在堆中地址不确定，不可能与方法区常量池中的s1地址相同。
        System.out.println(s1 == s9);  // false

        // s4 == s5已经不用解释了，绝对不相等，二者都在堆中，但地址不同。
        System.out.println(s4 == s5);  // false

        // s1 == s6这两个相等完全归功于intern方法，s5在堆中，内容为Hello ，intern方法会尝试将Hello字符串添加到常量池中，并返回其在常量池中的地址，
        // 因为常量池中已经有了Hello字符串，所以intern方法直接返回地址；而s1在编译期就已经指向常量池了，因此s1和s6指向同一地址，相等。
        System.out.println(s1 == s6);  // true


        String a = "str";
        String b = new String("str");

        // 比较的是栈中的引用地址 false
        System.out.println(a == b);
        // 比较的是堆内存中 的对象 true
        System.out.println("a.equals(b):"+ a.equals(b));
    }
}
